import type { ChangeEvent } from 'react';
import type { PDFImage } from '@pdfme/pdf-lib';
import type { Plugin } from '@pdfme/common';
import type { Schema } from '@pdfme/common';
import type * as CSS from 'csstype';
import { px2mm } from '@pdfme/common';
import { Image } from 'lucide';
import {
  convertForPdfLayoutProps,
  addAlphaToHex,
  isEditable,
  readFile,
  createSvgStr,
} from '../utils.js';
import { DEFAULT_OPACITY } from '../constants.js';
import { getImageDimension } from './imagehelper.js';

const getCacheKey = (schema: Schema, input: string) => `${schema.type}${input}`;
const fullSize = { width: '100%', height: '100%' };
const defaultValue =
  '';

type ImageSchema = Schema;

const imageSchema: Plugin<ImageSchema> = {
  pdf: async (arg) => {
    const { value, schema, pdfDoc, page, _cache } = arg;
    if (!value) return;

    const inputImageCacheKey = getCacheKey(schema, value);
    let image = _cache.get(inputImageCacheKey) as PDFImage;
    if (!image) {
      const isPng = value.startsWith('data:image/png;');
      image = await (isPng ? pdfDoc.embedPng(value) : pdfDoc.embedJpg(value));
      _cache.set(inputImageCacheKey, image);
    }

    const _schema = { ...schema, position: { ...schema.position } };
    const dimension = getImageDimension(value);
    const imageWidth = px2mm(dimension.width);
    const imageHeight = px2mm(dimension.height);
    const boxWidth = _schema.width;
    const boxHeight = _schema.height;

    const imageRatio = imageWidth / imageHeight;
    const boxRatio = boxWidth / boxHeight;

    if (imageRatio > boxRatio) {
      _schema.width = boxWidth;
      _schema.height = boxWidth / imageRatio;
      _schema.position.y += (boxHeight - _schema.height) / 2;
    } else {
      _schema.width = boxHeight * imageRatio;
      _schema.height = boxHeight;
      _schema.position.x += (boxWidth - _schema.width) / 2;
    }

    const pageHeight = page.getHeight();
    const lProps = convertForPdfLayoutProps({ schema: _schema, pageHeight });
    const { width, height, rotate, position, opacity } = lProps;
    const { x, y } = position;

    const drawOptions = { x, y, rotate, width, height, opacity };
    page.drawImage(image, drawOptions);
  },
  ui: (arg) => {
    const {
      value,
      rootElement,
      mode,
      onChange,
      stopEditing,
      tabIndex,
      placeholder,
      theme,
      schema,
    } = arg;
    const editable = isEditable(mode, schema);
    const isDefault = value === defaultValue;

    const container = document.createElement('div');
    const backgroundStyle = placeholder ? `url(${placeholder})` : 'none';
    const containerStyle: CSS.Properties = {
      ...fullSize,
      backgroundImage: value ? 'none' : backgroundStyle,
      backgroundSize: `contain`,
      backgroundRepeat: 'no-repeat',
      backgroundPosition: 'center',
    };
    Object.assign(container.style, containerStyle);
    container.addEventListener('click', (e) => {
      if (editable) {
        e.stopPropagation();
      }
    });
    rootElement.appendChild(container);

    // image tag
    if (value) {
      const img = document.createElement('img');
      const imgStyle: CSS.Properties = {
        height: '100%',
        width: '100%',
        borderRadius: 0,
        objectFit: 'contain',
      };
      Object.assign(img.style, imgStyle);
      img.src = value;
      container.appendChild(img);
    }

    // remove button
    if (value && !isDefault && editable) {
      const button = document.createElement('button');
      button.textContent = 'x';
      const buttonStyle: CSS.Properties = {
        position: 'absolute',
        top: 0,
        left: 0,
        zIndex: 1,
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        color: '#333',
        background: '#f2f2f2',
        borderRadius: '2px',
        border: '1px solid #767676',
        cursor: 'pointer',
        height: '24px',
        width: '24px',
      };
      Object.assign(button.style, buttonStyle);
      button.addEventListener('click', () => {
        if (onChange) onChange({ key: 'content', value: '' });
      });
      container.appendChild(button);
    }

    // file input
    if ((!value || isDefault) && editable) {
      const label = document.createElement('label');
      const labelStyle: CSS.Properties = {
        ...fullSize,
        display: editable ? 'flex' : 'none',
        position: 'absolute',
        top: 0,
        backgroundColor: editable || value ? addAlphaToHex(theme.colorPrimaryBg, 30) : 'none',
        cursor: 'pointer',
      };
      Object.assign(label.style, labelStyle);
      container.appendChild(label);
      const input = document.createElement('input');
      const inputStyle: CSS.Properties = {
        ...fullSize,
        position: 'absolute',
        top: '50%',
        left: '50%',
        width: '180px',
        height: '30px',
        marginLeft: '-90px',
        marginTop: '-15px',
      };
      Object.assign(input.style, inputStyle);
      input.tabIndex = tabIndex || 0;
      input.type = 'file';
      input.accept = 'image/jpeg, image/png';
      input.addEventListener('change', (event: Event) => {
        const changeEvent = event as unknown as ChangeEvent<HTMLInputElement>;
        readFile(changeEvent.target.files)
          .then((result) => {
            if (onChange) onChange({ key: 'content', value: result as string });
          })
          .catch((error) => {
            console.error('Error reading file:', error);
          });
      });
      input.addEventListener('blur', () => {
        if (stopEditing) stopEditing();
      });
      label.appendChild(input);
    }
  },
  propPanel: {
    schema: {},
    defaultSchema: {
      name: '',
      type: 'image',
      content: defaultValue,
      position: { x: 0, y: 0 },
      width: 40,
      height: 40,
      // If the value of "rotate" is set to undefined or not set at all, rotation will be disabled in the UI.
      // Check this document: https://pdfme.com//docs/custom-schemas#learning-how-to-create-from-pdfmeschemas-code
      rotate: 0,
      opacity: DEFAULT_OPACITY,
    },
  },
  icon: createSvgStr(Image),
};

export default imageSchema;
